
/* Oscar Medina
 * http://sharepointace.com/blog
 * http://www.codeplex.com/CQWPRCE
 * 
 * This is an enhanced version of the ContentByQueryWebPart
 * It adds support for showing recurring calendar events which do not
 * show on the OOTB version.
 */
using System;
using System.Data;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Xml.Serialization;
using System.ComponentModel;
using Microsoft.SharePoint;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Utilities;
using System.Threading;
using Microsoft.Office.Server.Diagnostics;
using System.Web;
using System.Runtime.InteropServices;
using System.Web.UI.HtmlControls;
using Microsoft.SharePoint.WebControls;
using System.IO;

namespace SharePointAce.SharePoint.WebParts
{
    
    public class SharePointAceCQWPRCE : Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart
    {
        #region Private variables
        private string _customViewFieldsOverride;
        private string _customQueryOverride;
        private string _itemXslLinkOverride;
        private string _mainXslLinkOverride;
        private bool _useQueryString;
        private bool _showRecurringCalendarEvents;
        private const string _expandRecurringEventsCAML = "<Where><DateRangesOverlap><FieldRef Name=\"EventDate\" /><FieldRef Name=\"EndDate\" /><FieldRef Name=\"RecurrenceID\" /><Value Type=\"DateTime\"><Month /></Value></DateRangesOverlap></Where>";
        internal int groupByItemLimit;
        #endregion

        #region Constants

        private const string ITEM_XSL_LINK = "/Style Library/XSL Style Sheets/ItemStyle.xsl";
        private const string MAIN_XSL_LINK = "/Style Library/XSL Style Sheets/ContentQueryMain.xsl";
        private const string TITLE_COLUMN_NAME = "{fa564e0f-0c70-4ab9-b863-0177e6ddd247}";
        #endregion

        #region WebPart Browseable Properties
        [Personalizable(), WebBrowsable(true)]
        public string customViewFieldsOverride
        {
            get { return _customViewFieldsOverride; }
            set
            {
                _customViewFieldsOverride = value.Trim();
            }
        }

        [Personalizable(), WebBrowsable(true)]
        public string customQueryOverride
        {
            get { return _customQueryOverride; }
            set
            {
                _customQueryOverride = value.Trim();

            }
        }
 
        [Personalizable(), WebBrowsable(true)]
        public string ItemXslLinkOverride
        {
            get
            {
                if (!string.IsNullOrEmpty(_itemXslLinkOverride))
                {
                    return _itemXslLinkOverride;
                }
                else
                {
                    return ITEM_XSL_LINK;
                }
            }
            set { _itemXslLinkOverride = value.Trim(); }
        }

        [Personalizable(), WebBrowsable(true)]
        public string MainXslLinkOverride
        {
            get
            {
                if (!string.IsNullOrEmpty(_mainXslLinkOverride))
                {
                    return _mainXslLinkOverride;
                }
                else
                {
                    return MAIN_XSL_LINK;
                }
            }
            set { _mainXslLinkOverride = value.Trim(); }
        }

        [Personalizable(), WebBrowsable(true)]
        public bool useQueryString
        {
            get { return _useQueryString; }
            set
            {
                _useQueryString = value;
            }
        }

        [Personalizable(), WebBrowsable(true)]
        public bool showRecurringCalendarEvents
        {
            get { return _showRecurringCalendarEvents; }
            set
            {
                _showRecurringCalendarEvents = value;
            }
        }
        [WebBrowsable(false), DefaultValue(50), Personalizable(PersonalizationScope.Shared), Category("General")]
        public int GroupByItemLimit
        {
            get { return groupByItemLimit; }
            set { groupByItemLimit = value; }
        }
        #endregion

        
        public SharePointAceCQWPRCE()
        {
            this.ExportMode = WebPartExportMode.All;
        }

        protected override void OnInit(EventArgs e)
        {
            QueryScope scope = CurrentQueryScope();
            this.UseCopyUtil = false;

            //for now only process custom data when using List and SiteCollection Scopes
            if (this.showRecurringCalendarEvents && (scope == QueryScope.List))
            {
                this.ProcessDataDelegate += new ProcessData(GetListCalendarRecurringEvents);
            }
            else if (this.showRecurringCalendarEvents && (scope == QueryScope.SiteCollection))
            {
                this.ProcessDataDelegate += new ProcessData(GetSiteCollectionCalendarRecurringEvents);
            }
                base.OnInit(e);
        }

        protected override void OnLoad(EventArgs e)
        {
           this.Title = "SharePointAce - Extended CQWP";
           base.OnLoad(e);
        }
        protected override void CreateChildControls()
        {
         
            if (!string.IsNullOrEmpty(customQueryOverride))
            { this.QueryOverride = this.customQueryOverride; }
            
            this.CommonViewFields = customViewFieldsOverride;
            
            this.ItemXslLink = ItemXslLinkOverride;
            this.MainXslLink = MainXslLinkOverride;

            base.CreateChildControls();

            this.QueryOverride = string.Empty;
            this.CommonViewFields = string.Empty;
            
        }

        protected DataTable GetListCalendarRecurringEvents(DataTable dt)
        {
            DataTable newDataTable = dt.Copy();
            try
            {

                //clear the table data so we can populate with our own data based on our query
                newDataTable.Clear();

                //add a custom column to hold the link to the calendar event
                //newDataTable.Columns.Add("LinkToCalendarItem");

                // Get the Events list
                SPWeb web = SPControl.GetContextWeb(Context);
                SPList calendarList = web.Lists[this.ListName];

                // Construct a query that expands recurring events
                SPQuery query = new SPQuery();
                query.ExpandRecurrence = true;
                query.Query += _expandRecurringEventsCAML;
                query.CalendarDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);

                // Returns all items (including recurrence instances) that
                // would appear in the calendar view for the current month
                SPListItemCollection calendarItems = calendarList.GetItems(query);

                foreach (SPListItem item in calendarItems)
                {
                    DataRow myRow = newDataTable.NewRow();
                    //myRow.ItemArray = dt.Rows[1].ItemArray;
                    myRow[TITLE_COLUMN_NAME] = item["Title"].ToString();
                    myRow["EventDate"] = item["EventDate"].ToString();
                    myRow["ProjectProperty.Title"] = item.Web.Title;
                    myRow["WebId"] = item.Web.ID.ToString();
                    myRow["ListId"] = item.ParentList.ID.ToString();
                    myRow["ID"] = item.RecurrenceID;
                    myRow["LinkToCalendarItem"] = item.Web.Url + "/" + item.ParentList.RootFolder.Url + "/DispForm.aspx?ID=" + item.RecurrenceID;

                    newDataTable.Rows.Add(myRow);
                }

            }
            catch (Exception e)
            {
                File.AppendAllText("C:\\SharePointAceWebPartLog.txt", e.Message + " " + e.StackTrace + "\r"+ e.Source);
            }
            return newDataTable;
        }
        /// <summary>
        /// Used to recursively retrieve all Events for all webs on this Site Collection
        /// </summary>
        /// <param name="dt"></param>
        /// <returns>DataTable populated with data to pass to XSL</returns>
        protected DataTable GetSiteCollectionCalendarRecurringEvents(DataTable dt)
        {
            DataTable newDataTable = dt.Copy();

            //clear the table data so we can populate with our own data based on our query
            newDataTable.Clear();

            //add a custom column to hold the link to the calendar event
            //newDataTable.Columns.Add("LinkToCalendarItem");

            // Get the Events list
            SPSite site = SPControl.GetContextSite(Context);
            SPWeb web = site.RootWeb;

            // Construct a query that expands recurring events
            SPQuery query = new SPQuery();
            query.ExpandRecurrence = true;
            query.Query += _expandRecurringEventsCAML;
            query.CalendarDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
            List<SPList> foundLists = GetAllSiteCollectionEventTypeLists();

            foreach (SPList eventList in foundLists)
            {
                // Returns all items (including recurrence instances) that
                // would appear in the calendar view for the current month
                SPListItemCollection calendarItems = eventList.GetItems(query);
                
                foreach (SPListItem item in calendarItems)
                {
                    DataRow myRow = newDataTable.NewRow();
                    myRow.ItemArray = dt.Rows[1].ItemArray;
                    myRow[TITLE_COLUMN_NAME] = item["Title"].ToString();
                    myRow["EventDate"] = item["EventDate"].ToString();
                    myRow["ProjectProperty.Title"] = eventList.ParentWeb.Title;
                    myRow["WebId"] = eventList.ParentWeb.ID.ToString();
                    myRow["ListId"] = eventList.ID.ToString();
                    myRow["ID"] = item.RecurrenceID;
                    myRow["LinkToCalendarItem"] = item.Web.Url + "/" + item.ParentList.RootFolder.Url + "/DispForm.aspx?ID=" + item.RecurrenceID;

                    newDataTable.Rows.Add(myRow);
                }
            }

            #region  Group Data By the Group Field user specified
            /*
            try
            {
                string groupByName = this.GroupBy;

                if (!String.IsNullOrEmpty(groupByName))
                {
                    Dictionary<string, int> occurrences = new Dictionary<string, int>();
                    List<int> rowIndexesToDelete = new List<int>();

                    // Iterate rows from beginning to end
                    int counter = 0;
                    while (counter < newDataTable.Rows.Count)
                    {
                        DataRow dr = newDataTable.Rows[counter];
                        string groupByValue = dr[groupByName].ToString();

                        // Handle row's GroupBy occurrence
                        if (occurrences.ContainsKey(groupByValue))
                        {
                            occurrences[groupByValue] += 1;
                        }
                        else
                        {
                            occurrences.Add(groupByValue, 1);
                        }

                        // Test occurence limit
                        if (occurrences[groupByValue] > groupByItemLimit)
                        {
                            // Delete row
                            dr.Delete();

                            // Do NOT increment counter because the next row receives the current counter as index
                        }
                        else
                        {
                            // Increment counter
                            counter++;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // TODO Handle exception
            }
             * */
            #endregion
            
            return newDataTable;
        }

        /// <summary>
        /// Gets All Event Type Lists from the Site Collection Webs
        /// </summary>
        /// <returns>Generic List with SPList Objects containing all Event Type Lists found</returns>
        protected List<SPList> GetAllSiteCollectionEventTypeLists()
        {
            SPSite site = SPContext.Current.Site;
            SPWebCollection webs = site.AllWebs;
            List<SPList> returnedLists = new List<SPList>();
           
            foreach (SPWeb web in webs)
            {
                foreach (SPList list in web.Lists)
                {
                    if (list.BaseTemplate == SPListTemplateType.Events)
                    {
                        returnedLists.Add(list);
                    }
                }
            }
            return returnedLists;
        }


        #region public override ToolPart[] GetToolParts()

        public override ToolPart[] GetToolParts()
        {
            List<ToolPart> toolParts = new List<ToolPart>(base.GetToolParts());
            toolParts.Add(new SharePointAceContentByQueryWebPartToolPart());
            

            return toolParts.ToArray();

        }

        #endregion

    }

}
